﻿/**
 *
 * at_client.c
 *
 *  Created on: 2019-10-21
 *      Author: chenshisheng
 */

#include "at_client.h"
#include "ctimer.h"
#include "serial.h"
#include "power_manager.h"
#include "stream/serial_stream.h"

#include <stdarg.h>
#include <ctype.h>

#define _SERIAL Serial_LP1

#define FRAME_TIMEOUT 100

enum _State
{
    _State_Idle,
    _State_Receiving,
    _State_Ok,
    _State_Error,
    _State_Timeout,
};

struct _ResultDetector
{
    int step;
};

enum _Result
{
    _Result_Executing,
    _Result_Ok,
    _Result_Error,
};

static struct _RxCtrl
{
    bool isExecutingCmd;
    enum _State state;
    unsigned int rxSize;
    uint8_t buf[AT_CLIENT_RX_BUF_SIZE];
    Ctimer_t frame;
    Ctimer_t respond;
    struct _ResultDetector okDetector;
    struct _ResultDetector errorDetector;
} _rxCtrl;

static const char *_okText = "\r\nOK\r\n";
static const char *_errorText = "\r\nERROR\r\n";
static char _frameRespond[AT_CLIENT_RX_BUF_SIZE + 1];
static SerialStream_t _stream;

static void _RestartRx(void);
static bool _RxByte(Serial_t serial, uint8_t b);

void AtClient_Init(void)
{
    _rxCtrl.isExecutingCmd = false;

    SerialStream_Init(&_stream, _SERIAL);
    Serial_SetCallbacks(_SERIAL, NULL, NULL, _RxByte);
    _RestartRx();
}

static void _RestartRx(void)
{
    Ctimer_Stop(&_rxCtrl.frame);

    _rxCtrl.rxSize = 0;
    _rxCtrl.okDetector.step = 0;
    _rxCtrl.errorDetector.step = 0;
    _rxCtrl.state = _State_Idle;
}

static void _OnFrameTimeout(int data)
{
    if(_rxCtrl.isExecutingCmd)
    {
        _rxCtrl.state = _State_Error;
    }
    else
    {
        _RestartRx();
    }
}

static void _OnRespondTimeout(int data)
{

    if(_rxCtrl.rxSize == 0)
    {
        _rxCtrl.state = _State_Timeout;
    }
    else
    {
        _rxCtrl.state = _State_Error;
    }
}

int AtClient_Send(int respondTimeoutMs, const char *fmt, ...)
{
    int ret = 0;
    va_list args;

    while(_rxCtrl.state == _State_Receiving)
    {
        PowerManager_Sleep();
    }

    _rxCtrl.isExecutingCmd = true;
    _RestartRx();

    debug("AtClient: Executing: ");
    va_start(args, fmt);
    vdebug(fmt, args);
    va_end(args);
    debug(" ... ");

    va_start(args, fmt);
    chvprintf(& _stream, fmt, args);
    chprintf(&_stream, "\r\n");
    va_end(args);

    if(respondTimeoutMs == 0)
    {
        return 0;
    }

    _rxCtrl.rxSize = 0;
    Ctimer_Start(&_rxCtrl.respond, respondTimeoutMs, _OnRespondTimeout,
            CTIMER_FLAG_URGEN | CTIMER_FLAG_ONCE, 0);
    while(_rxCtrl.state < _State_Ok)
    {
        PowerManager_Sleep();
        LL_IWDG_ReloadCounter(IWDG);
    }

    _frameRespond[0] = '\0';
    if(_rxCtrl.state == _State_Ok)
    {
        memcpy(_frameRespond, _rxCtrl.buf, _rxCtrl.rxSize);
        _frameRespond[_rxCtrl.rxSize] = '\0';
//        debug("RSP: %s\n", _frameRespond);
    }
    else if(_rxCtrl.state == _State_Error)
    {
        ret = -1;
    }
    else if(_rxCtrl.state == _State_Timeout)
    {
        ret = -2;
    }
    else
    {
        ret = -3;
    }

    _rxCtrl.isExecutingCmd = false;
    _RestartRx();

    debug("Result: %d\n", ret);
    return ret;
}

void AtClient_AbortExecution(void)
{
    if(_rxCtrl.isExecutingCmd)
    {
        _rxCtrl.state = _State_Error;
        Ctimer_Stop(&_rxCtrl.respond);
    }
}

#if 0
int AtClient_ParseRespArgs(int lineIndex, const char *fmt, ...)
{
    va_list args;
    int n;
    UNUSED(lineIndex);

    va_start(args, fmt);
    n = vsscanf(_frameRespond, fmt, args);
    va_end(args);

    return n;
}
#endif

char *AtClient_RespValue(void)
{
    char *p, *str;

    str = strchr(_frameRespond, '+'); // 是否是命令的回复
    if(str == NULL)
    {
        str = _frameRespond;
        while(isspace(*str))
        {
            str ++;
        }
    }
    else
    {
        str ++;
        p = strchr(str, ':'); // 从 ':'之后开始取
        if(p == NULL)
        {
            return "";
        }

        str = p + 1;
    }

    p = strchr(str, '\r');  // 只取一行
    if(p != NULL)
    {
        *p = '\0';
    }

    return str;
}

static bool _IsEnded(struct _ResultDetector *detector, const char *endText, char c)
{
    int len;
    bool ended = false;

    len = strlen(endText);
    if(detector->step < len)
    {
        if(c == endText[detector->step])
        {
            detector->step ++;
            if(detector->step == len)
            {
                ended = true;
            }
        }
        else if(c == endText[0])
        {
            detector->step = 1;
        }
        else
        {
            detector->step = 0;
        }
    }
    else
    {
        ended = true;
    }

    return ended;
}

static enum _Result _ResultDetect(char c)
{
    if(_IsEnded(&_rxCtrl.okDetector, _okText, c))
    {
        return _Result_Ok;
    }

    if(_IsEnded(&_rxCtrl.errorDetector, _errorText, c))
    {
        return _Result_Error;
    }

    return _Result_Executing;
}

static bool _RxByte(Serial_t serial, uint8_t b)
{
    enum _Result result;

    if(_rxCtrl.state == _State_Idle)
    {
        _rxCtrl.state = _State_Receiving;
    }

    if(!Ctimer_IsRunning(&_rxCtrl.frame))
    {
        Ctimer_Start(
                &_rxCtrl.frame,
                FRAME_TIMEOUT,
                _OnFrameTimeout,
                CTIMER_FLAG_URGEN | CTIMER_FLAG_ONCE,
                0);
    }
    else
    {
        Ctimer_Reset(&_rxCtrl.frame);
    }

    if(_rxCtrl.state > _State_Receiving)
    {
        return false;
    }

    if((_rxCtrl.rxSize >= AT_CLIENT_RX_BUF_SIZE))
    {
        return false;
    }

    _rxCtrl.buf[_rxCtrl.rxSize] = b;
    _rxCtrl.rxSize ++;
    if(_rxCtrl.isExecutingCmd)
    {
        result = _ResultDetect(b);
        if(result != _Result_Executing)
        {
            _rxCtrl.state = (result == _Result_Ok) ? _State_Ok : _State_Error;
            Ctimer_Stop(&_rxCtrl.frame);
        }
    }
    else
    {
        if((b == '\n') && (_rxCtrl.rxSize > 2))
        {
            MsgQueue_Send(MsgQueue_Id_AtEvent, _rxCtrl.buf, _rxCtrl.rxSize);
            _RestartRx();
        }
    }

    return false;
}

int AtClient_Split(char *str, char split, char *argv[], int argcMax)
{
    int argc;
    char *p;

    for(argc = 0; argc < argcMax; str = p + 1)
    {
        p = strchr(str, split);
        if(p == NULL)
        {
            break;
        }

        *p = '\0';
        argv[argc++] = str;
    }

    p = strchr(str, '\r');
    if(p != NULL)
    {
        *p = '\0';
    }

    argv[argc++] = str;

    return argc;
}

int AtClient_RespValueList(char *argv[], int argcMax)
{
    char *valueText;

    valueText = AtClient_RespValue();
    return AtClient_Split(valueText, ',', argv, argcMax);
}

int AtClient_Printf(const char *fmt, ...)
{
    va_list args;
    int ret;

    va_start(args, fmt);
    ret = chvprintf(&_stream, fmt, args);
    va_end(args);

    return ret;
}
